#include "GameManager.h"
#include "ConfigManager.h"
#include "ResourcesManager.h"
#include "WaveManager.h"
#include "EnemyManager.h"
#include "TowerManager.h"
#include "BulletManager.h"
#include "PlayerManager.h"
#include "CoinManager.h"
#include "HomeManager.h"


GameManager::GameManager()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
    Mix_Init(MIX_INIT_MP3);
    TTF_Init();
    Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
    SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");

    ConfigManager &configManager = ConfigManager::Instance();
    configManager.map.Load("config/map.csv");
    configManager.LoadGameConfig("config/game_config.json");
    configManager.LoadLevelConfig("config/level.json");

    window = SDL_CreateWindow("你好世界", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                              configManager.basic_template.window_width,
                              configManager.basic_template.window_height, SDL_WINDOW_SHOWN);
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC |
                                              SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);

    ResourcesManager::Instance().LoadFromFile(renderer);
    GenerateTileMapTexture();
    placePanel = new PlacePanel();
    upgradePanel = new UpgradePanel();
    banner = new Banner();
}

GameManager::~GameManager()
{
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    TTF_Quit();
    Mix_Quit();
    IMG_Quit();
    SDL_Quit();
}

int GameManager::Run(int argc, char **argv)
{
    unsigned long long lastCounter = SDL_GetPerformanceCounter();
    const unsigned long long counterFreq = SDL_GetPerformanceFrequency();

    while (!isQuit) {
        while (SDL_PollEvent(&event)) {
            OnInput();
        }

        unsigned long long currentCounter = SDL_GetPerformanceCounter();
        double delta = (double) (currentCounter - lastCounter) / counterFreq;
        lastCounter = currentCounter;
        if (delta * 1000 < 1000.0 / 60) {
            SDL_Delay((Uint32) (1000.0 / 60 - delta * 1000));
        }

        OnUpdate(delta);

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        OnRender();

        SDL_RenderPresent(renderer);
    }
    return 0;
}

void GameManager::InitAssert(bool flag, char *errmsg)
{
    if (flag) return;
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "游戏启动失败", errmsg, window);
    exit(-1);
}

void GameManager::OnInput()
{
    static SDL_Point posCenter;
    static SDL_Point idxTileSelected;
    static ConfigManager &instance = ConfigManager::Instance();

    switch (event.type) {
        case SDL_QUIT:
            isQuit = true;
            break;
        case SDL_MOUSEBUTTONDOWN:
            if (instance.is_game_over)
                break;
            if (GetCursorIdxTile(idxTileSelected, event.motion.x, event.motion.y)) {
                GetSelectedTileCenterPos(posCenter, idxTileSelected);

                if (CheckHome(idxTileSelected)) {
                    upgradePanel->SetIdxTile(idxTileSelected);
                    upgradePanel->SetCenterPos(posCenter);
                    upgradePanel->Show();
                } else if (CanPlaceTower(idxTileSelected)) {
                    placePanel->SetIdxTile(idxTileSelected);
                    placePanel->SetCenterPos(posCenter);
                    placePanel->Show();
                }
            }
            break;
        default:
            break;
    }

    if (!instance.is_game_over) {
        placePanel->OnInput(event);
        upgradePanel->OnInput(event);
        PlayerManager::Instance().OnInput(event);
    }
}

void GameManager::OnUpdate(double delta)
{
    static bool isGameOverLastTick = false;
    static ConfigManager &instance = ConfigManager::Instance();
    if (!instance.is_game_over) {
        statusBar.OnUpdate(renderer, CoinManager::Instance().GetCurrentCoinNum());
        placePanel->OnUpdate(renderer);
        upgradePanel->OnUpdate(renderer);
        WaveManager::Instance().OnUpdate(delta);
        EnemyManager::Instance().OnUpdate(delta);
        BulletManager::Instance().OnUpdate(delta);
        TowerManager::Instance().OnUpdate(delta);
        PlayerManager::Instance().OnUpdate(delta);
        return;
    }

    if (!isGameOverLastTick && instance.is_game_over) {
        static const ResourcesManager::SoundPool &soundPool = ResourcesManager::Instance().soundPool;

        Mix_FadeOutMusic(1500);
        Mix_PlayChannel(-1, soundPool.find(instance.is_game_win ? ResID::Sound_Win : ResID::Sound_Loss)->second, 0);
    }

    isGameOverLastTick = instance.is_game_over;

    banner->OnUpdate(delta);
    if (banner->CheckEndDisplay())
        isQuit = true;
}

void GameManager::OnRender()
{
    ConfigManager &configManager = ConfigManager::Instance();
    SDL_Rect &rectDest = configManager.rectTileMap;
    SDL_RenderCopy(renderer, texTileMap, nullptr, &rectDest);

    statusBar.OnRender(renderer, HomeManager::Instance().GetCurrentHpNumber());
    EnemyManager::Instance().OnRender(renderer);
    BulletManager::Instance().OnRender(renderer);
    TowerManager::Instance().OnRender(renderer);
    if (!configManager.is_game_over) {
        placePanel->OnRender(renderer);
        upgradePanel->OnRender(renderer);
        PlayerManager::Instance().OnRender(renderer);
        return;
    }

    int widthScreen, heightScreen;
    SDL_GetWindowSizeInPixels(window, &widthScreen, &heightScreen);
    banner->SetCenterPosition({(double)widthScreen / 2, (double)heightScreen / 2 });
    banner->OnRender(renderer);
}

bool GameManager::GenerateTileMapTexture()
{
    const Map &map = ConfigManager::Instance().map;
    const TileMap &tileMap = map.GetTileMap();
    SDL_Rect &rectTileMap = ConfigManager::Instance().rectTileMap;
    SDL_Texture *texTileSet = ResourcesManager::Instance().texturePool[ResID::Tex_Tileset];

    int widthTexTileSet, heightTexTileSet; // 纹理的宽高
    SDL_QueryTexture(texTileSet, nullptr, nullptr, &widthTexTileSet, &heightTexTileSet);
    int numTileSingleLine = (int) std::ceil((double) widthTexTileSet / SIZE_TILE); // 获取一行有多少个单元格

    int widthTexTileMap, heightTexTileMap; // 地图纹理的宽高
    widthTexTileMap = (int) map.GetWidth() * SIZE_TILE;
    heightTexTileMap = (int) map.GetHeight() * SIZE_TILE;
    texTileMap = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
                                   SDL_TEXTUREACCESS_TARGET, widthTexTileMap, heightTexTileMap);
    if (!texTileMap) return false;

    // 居中显示
    ConfigManager &config = ConfigManager::Instance();
    rectTileMap.x = (config.basic_template.window_width - widthTexTileMap) / 2;
    rectTileMap.y = (config.basic_template.window_height - heightTexTileMap) / 2;
    rectTileMap.w = widthTexTileMap;
    rectTileMap.h = heightTexTileMap;

    SDL_SetTextureBlendMode(texTileMap, SDL_BLENDMODE_BLEND); // 设置为混合模式
    SDL_SetRenderTarget(renderer, texTileMap); // 设置渲染目标

    for (int y = 0; y < map.GetHeight(); y++) {
        for (int x = 0; x < map.GetWidth(); x++) {
            SDL_Rect rect_src;
            const Tile &tile = tileMap[y][x];
            const SDL_Rect &rect_dst = {x * SIZE_TILE, y * SIZE_TILE, SIZE_TILE, SIZE_TILE};
            rect_src = {(tile.terrian % numTileSingleLine) * SIZE_TILE,
                        (tile.terrian / numTileSingleLine) * SIZE_TILE,
                        SIZE_TILE, SIZE_TILE};
            SDL_RenderCopy(renderer, texTileSet, &rect_src, &rect_dst);

            if (tile.decoration >= 0) {
                rect_src = {(tile.decoration % numTileSingleLine) * SIZE_TILE,
                            (tile.decoration / numTileSingleLine) * SIZE_TILE,
                            SIZE_TILE, SIZE_TILE};
                SDL_RenderCopy(renderer, texTileSet, &rect_src, &rect_dst);
            }
        }
    }
    const SDL_Point &idx_home = map.GetIdxHome();
    const SDL_Rect rect_dst = {idx_home.x * SIZE_TILE, idx_home.y * SIZE_TILE, SIZE_TILE, SIZE_TILE};
    SDL_RenderCopy(renderer, ResourcesManager::Instance().texturePool[ResID::Tex_Home],
                   nullptr, &rect_dst);

    SDL_SetRenderTarget(renderer, nullptr); // 设置渲染目标为窗口
    return true;
}

bool GameManager::CheckHome(const SDL_Point &idxTileSelected) const
{
    static const Map &map = ConfigManager::Instance().map;
    static const SDL_Point &idxHome = map.GetIdxHome();

    return (idxHome.x == idxTileSelected.x && idxHome.y == idxTileSelected.y);
}

bool GameManager::GetCursorIdxTile(SDL_Point &idxTileSelected, int screenX, int screenY) const
{
    static const Map &map = ConfigManager::Instance().map;
    static const SDL_Rect &rectTileMap = ConfigManager::Instance().rectTileMap;

    if (screenX < rectTileMap.x || screenX > rectTileMap.x + rectTileMap.w
        || screenY < rectTileMap.y || screenY > rectTileMap.x + rectTileMap.h)
        return false;

    idxTileSelected.x = std::min((screenX - rectTileMap.x) / SIZE_TILE, (int) map.GetWidth() - 1);
    idxTileSelected.y = std::min((screenY - rectTileMap.y) / SIZE_TILE, (int) map.GetHeight() - 1);

    return true;
}

bool GameManager::CanPlaceTower(const SDL_Point &idxTileSelected) const
{
    static const Map &map = ConfigManager::Instance().map;
    const Tile &tile = map.GetTileMap()[idxTileSelected.y][idxTileSelected.x];

    return (tile.decoration < 0 && tile.direction == Tile::Direction::None && !tile.hasTower);
}

void GameManager::GetSelectedTileCenterPos(SDL_Point &pos, const SDL_Point &idxTileSelected) const
{
    static const SDL_Rect &rectTileMap = ConfigManager::Instance().rectTileMap;

    pos.x = rectTileMap.x + idxTileSelected.x * SIZE_TILE + SIZE_TILE / 2;
    pos.y = rectTileMap.y + idxTileSelected.y * SIZE_TILE + SIZE_TILE / 2;
}

